package com.fa4j.system.login.service.impl;

import com.fa4j.common.base.exception.CommonRequestError;
import com.fa4j.common.base.exception.CommonServerError;
import com.fa4j.system.login.model.CaptchaSendCmd;
import com.fa4j.system.login.model.CaptchaSendResult;
import com.fa4j.system.login.model.CaptchaType;
import com.fa4j.system.login.service.CaptchaService;
import com.fa4j.system.settings.model.EmailProps;
import com.fa4j.system.settings.service.SystemSettingsService;
import com.fa4j.third.sms.model.ThirdSmsSendCmd;
import com.fa4j.third.sms.service.ThirdSmsService;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.util.Pair;
import org.springframework.mail.MailSender;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Validated
@Service
@RequiredArgsConstructor
public class CaptchaServiceImpl implements CaptchaService {

    private final RedisTemplate<String, String> redisTemplate;
    private final ThirdSmsService thirdSmsService;
    private final SystemSettingsService systemSettingsService;


    @Override
    public CaptchaSendResult sendCaptcha(CaptchaSendCmd cmd) {
        String code = generateCaptchaCode(cmd.getType(), cmd.getTarget());
        return switch (cmd.getType()) {
            case SMS_LOGIN, MODIFY_PHONE -> sendSmsCaptcha(cmd, code);
            case MODIFY_EMAIL -> sendEmailCaptcha(cmd, code);
        };
    }

    private CaptchaSendResult sendEmailCaptcha(CaptchaSendCmd cmd, String code) {
        Pair<MailSender, SimpleMailMessage> senderPair = systemSettingsService.getSettings(EmailProps.class)
                .map(this::buildMailSender)
                .orElseThrow(() -> CommonServerError.NOT_SETTING.build("邮箱"));
        SimpleMailMessage msg = new SimpleMailMessage(senderPair.getSecond());
        msg.setSubject("验证码" + cmd.getType().getName());
        msg.setTo(cmd.getTarget());
        msg.setText("验证码:" + code);
        senderPair.getFirst().send(msg);
        return new CaptchaSendResult();
    }

    private Pair<MailSender, SimpleMailMessage> buildMailSender(EmailProps props) {
        JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
        mailSender.setHost(props.getSmtpHost());
        mailSender.setPort(props.getSmtpPort());
        mailSender.setUsername(props.getUsername());
        mailSender.setPassword(props.getPassword());
        mailSender.setDefaultEncoding(StandardCharsets.UTF_8.name());
        SimpleMailMessage templateMessage = new SimpleMailMessage();
        templateMessage.setFrom(props.getAddress());
        return Pair.of(mailSender, templateMessage);
    }

    private CaptchaSendResult sendSmsCaptcha(CaptchaSendCmd cmd, String code) {
        ThirdSmsSendCmd sendCmd = new ThirdSmsSendCmd();
        sendCmd.setBizCode("SMS_CAPTCHA");
        sendCmd.setPhoneNumber(cmd.getTarget());
        sendCmd.setTemplateParams(Map.of("captcha", code));
        thirdSmsService.sendSms(sendCmd);
        return new CaptchaSendResult();
    }


    /**
     * 生成验证码
     *
     * @param type   验证码类型
     * @param target 目标
     * @return 验证码信息
     */
    private String generateCaptchaCode(CaptchaType type, String target) {
        String code = type.isOnlyNumber()
                ? RandomStringUtils.secure().nextNumeric(type.getLength())
                : RandomStringUtils.secure().nextAlphanumeric(type.getLength());
        String cacheKey = type + ":" + target;
        redisTemplate.opsForValue().set(cacheKey, code, type.getExpireMinutes(), TimeUnit.MINUTES);
        return code;
    }

    /**
     * 验证码验证
     *
     * @param type        验证码类型
     * @param target      验证码目标
     * @param captchaCode 验证码code
     */
    @Override
    public void validCaptcha(CaptchaType type, String target, String captchaCode) {
        if (captchaCode.length() != type.getLength()) {
            throw CommonRequestError.PARAM_INVALID.build("验证码");
        }
        String cacheKey = type + ":" + target;
        String code = redisTemplate.opsForValue().get(cacheKey);
        if (!Objects.equals(code, captchaCode)) {
            throw CommonRequestError.PARAM_ERROR.build("验证码");
        }
        redisTemplate.delete(cacheKey);
    }
}
